
#include "gsm.h"
#include "settings.h"

GSM::GSM(String phoneNumber) : 
_gprsSerial(7, 8) {
  _phoneNumber = phoneNumber;
  _powerPin = 9;
  _errorCount = 0;
  _lastPingTs = 0;
  _lastCheckSmsTs = 0;
  _smsReceived = false;
}

boolean GSM::isAlive() {
  _gprsSerial.print("AT\r");
  return _waitOk() == SEND_AT_OK;
}

boolean GSM::check() {
  // ping shield once in a minute
  boolean alive = true;
  unsigned long now = millis();
  if(_lastPingTs > now || now > _lastPingTs + 60000) {
    alive = isAlive();
    _lastPingTs = now;
  }

  if(alive && _errorCount < ERROR_LIMIT) {
    return false;
  }

  if(alive) {
    // turn it off
    power();
    delay(1000);
  }

  for(int n = 0 ; n < 3 ; n++) {
    power();
    if(isAlive()) {
      init();
      _errorCount = 0;
      return true;
    }
  }
  return true;
}

void GSM::power() 
{
  //Turn on GSM
  pinMode(_powerPin, OUTPUT); 

  digitalWrite(_powerPin,LOW);
  delay(1000);
  digitalWrite(_powerPin,HIGH);
  delay(2000);
  digitalWrite(_powerPin,LOW);
  delay(3000);

  _gprsSerial.begin(19200);

  delay(500);

}

void GSM::init() {

  _gprsSerial.print(F("RDY\r"));
  _wait("Call Ready", 5000);

  // select sms message format = text 
  _gprsSerial.print(F("AT+CMGF=1\r"));  
  _waitOk();
  // set flow control = software flow control
  _gprsSerial.print(F("AT+IFC=1,1\r"));  
  _waitOk();
  // select phonebook memory strorage = SIM
  _gprsSerial.print(F("AT+CPBS=\"SM\"\r"));
  _waitOk();
  // new sms message indications
  _gprsSerial.print(F("AT+CNMI=2,0\r"));
  _waitOk();
} 

boolean GSM::sendSMS(String msg) {
  // set receiver phone number
  _gprsSerial.print("AT+CMGS=\"" + _phoneNumber + "\"\r");
  _waitPrompt();  
  _gprsSerial.print(msg);
  _gprsSerial.print('\r');  
  _gprsSerial.write((char)26); // ctrl-z
  return _waitOk() == SEND_AT_OK;
}

String GSM::receiveSMS()  {

  _flush();

  unsigned long now = millis();
  if(_smsReceived || _lastCheckSmsTs > now || now > _lastCheckSmsTs + 300000) {
    _lastCheckSmsTs = now;
    _smsReceived = false;
    
    _gprsSerial.flush();

    _lastData[0] = '\0';

    _gprsSerial.print(F("AT+CMGR=1\r"));
    _waitOk();

    String sms = String(_lastData);

    if(sms.length() > 0) {
      _gprsSerial.print(F("AT+CMGD=1\r"));
      _waitOk();
    }

    return sms;  
  }
  return "";
}

void GSM::cosmConnect() {
  _gprsSerial.print(F("AT+CSTT=\"internet\"\r"));//start task and setting the APN,
  _waitOk();
  _gprsSerial.print(F("AT+CIICR\r"));//bring up wireless connection
  _waitOk();
  _gprsSerial.print(F("AT+CIFSR\r"));//get local IP adress
  _wait();
  _gprsSerial.print(F("AT+CIPSPRT=0\r"));
  _waitOk();
  _gprsSerial.print(F("AT+CIPSTART=\"tcp\",\"api.cosm.com\",\"8081\"\r"));
  _wait("CONNECT OK");//start up the connection
}

void GSM::cosmSend(String feed, int temperature, int humidity, int relayStatus, int errorCount) {
  _gprsSerial.print(F("AT+CIPSEND\r"));//begin send data to remote server
  _wait();
  _gprsSerial.print(F("{\"method\": \"put\",\"resource\": \"/feeds/"));
  _wait();
  _gprsSerial.print(feed);
  _wait();
  _gprsSerial.print(F("/\",\"params\" : {},\"headers\": {\"X-PachubeApiKey\": \""));//in here, you should replace your pachubeapikey
  _wait();
  _gprsSerial.print(F(COSM_KEY));
  _wait();
  _gprsSerial.print(F("\"},\"body\": {\"version\": \"1.0.0\",\"datastreams\": \r"));
  _wait();
  _gprsSerial.print(F("[{\"id\": \"T\",\"current_value\": \""));
  _gprsSerial.print(temperature);
  _wait();
  _gprsSerial.print(F("\"}, {\"id\": \"H\",\"current_value\": \""));
  _gprsSerial.print(humidity);
  _wait();
  _gprsSerial.print(F("\"}, {\"id\": \"R\",\"current_value\": \""));
  _gprsSerial.print(relayStatus);
  _wait();
  _gprsSerial.print(F("\"}, {\"id\": \"E\",\"current_value\": \""));
  _gprsSerial.print(errorCount);
  _wait();
  _gprsSerial.print(F("\"}]}}\r"));
  _wait();

  _gprsSerial.println((char)26);//sending
  _wait("SEND OK");
  _wait();
}


void GSM::cosmDisconnect() {
  _gprsSerial.print(F("AT+CIPCLOSE\r"));
  _wait("CLOSE OK");//close the connection
}

/**
 * SEND_AT_OK           - no errors
 * SEND_AT_NO_ANSWER    - no response from shield during timeout
 * SEND_AT_TIMEOUT      - expected response is not received during timeout
 * SEND_AT_ERROR        - shield returned 'ERROR' string
 */
int GSM::_wait(char* response, boolean crlf, long timeout) {

  // indicates that module returned +XXX: ....
  boolean isData = false;
  boolean responseReceived = false;

  boolean errorReceived = false;  
  char answerLine[ANSWER_LINE_LEN+1];
  byte index = 0;

  long endTs = millis() + timeout;

  while(timeout > 0 && !_gprsSerial.available()) {
    if(millis() > endTs) {
      Serial.println("ret=" + String(SEND_AT_NO_ANSWER));      
      _errorCount += ERROR_INCREMENT;
      return SEND_AT_NO_ANSWER;
    }
  }

  delay(100);

  while((timeout < 0 || millis() < endTs) && !(responseReceived || errorReceived)) {
    while(_gprsSerial.available()) {
      char ch = _gprsSerial.read();

      Serial.write(ch);

      if ('\r' == ch) {

        if (isData) {
          strcpy(_lastData, answerLine);
          isData = false;
        }      
        else {
          if (answerLine[0] == '+') {
            //if this line starts with "+CMT",
            //then next line is a sms body
            isData = true;
          }
          if (strncmp(answerLine, "+CMT", 4) == 0) {
            _smsReceived = true;
          }
        }

        Serial.println();        

        if(response != NULL) {
          responseReceived = responseReceived || strcmp(answerLine, response) == 0;
        }
        errorReceived = strstr(answerLine, "ERROR") != 0;

        index = 0;
        memset(answerLine, 0, ANSWER_LINE_LEN+1);

      }
      else if ('\n' != ch && index < ANSWER_LINE_LEN) {
        answerLine[index++] = ch;
      }

      if(!crlf && response != NULL) {
        responseReceived = responseReceived || strcmp(answerLine, response) == 0;
      }
    }
    if(response == NULL || timeout < 0) {
      responseReceived = true;
    }
  }

  int ret = SEND_AT_TIMEOUT;  
  if(errorReceived) {
    ret = SEND_AT_ERROR;
  }

  if(responseReceived) {
    ret = SEND_AT_OK;
  }

  if(ret == SEND_AT_OK) {
    if(_errorCount > 0) _errorCount--;
  } 
  else {
    _errorCount += ERROR_INCREMENT;
  }

  if(timeout > 0) {
    Serial.println("ret=" + String(ret));
  }
  
  return ret;

}









